#include <stdio.h>

#include "main.h"
#include "i2s.h"

#include "menu.h"
#include "audio.h"
#include "wav.h"
#include "flac.h"
#include "ape.h"

#include "wm8978.h"
#include "st7789.h"

AUDIO_DEV	audio_dev;

AUDIO_DEV	*pAudio = &audio_dev;

int		Is_Process_buffer;
int		Is_finished;
int		Is_End_chunk;

uint8_t		Is_Busy;

static void I2s_Start(void);
static void I2S_Pause(void);
static void I2S_Resume(void);
static void I2S_Stop(void);
static void I2S_Abort(void);
static void I2s_Transfer(uint16_t *pData, uint16_t Size);

static void error_process_buffer(void);

static void F4_I2S_Transmit_DMA(I2S_HandleTypeDef *hi2s, uint16_t *pData, uint16_t Size);

void I2S_44K_Config(void);
void I2S_48K_Config(void);
void I2S_96K_Config(void);

/**
  * @brief  The application .
  * @retval void
  */
static void Enable_Tim6(void)
{
	__HAL_RCC_TIM10_CLK_ENABLE();
	HAL_NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);

	// HAL_TIM_Base_Start_IT(&htim6);
}

/**
  * @brief  The application .
  * @retval void
  */
static void Disable_Tim6(void)
{
	__HAL_RCC_TIM10_CLK_DISABLE();
	HAL_NVIC_DisableIRQ(TIM1_UP_TIM10_IRQn);

	// HAL_TIM_Base_Stop_IT(&htim6);
}

/**
  * @brief  The application .
  * @retval void
  */
void audio_Start(void)
{
	I2s_Start();
	menu_Start();

	Enable_Tim6();
}

/**
  * @brief  The application .
  * @retval void
  */
void audio_Pause(void)
{
	I2S_Pause();
	menu_Pause();

	Disable_Tim6();
}

/**
  * @brief  The application .
  * @retval void
  */
void audio_Resume(void)
{
	I2S_Resume();
	menu_Resume();

	Enable_Tim6();
}

/**
  * @brief  The application .
  * @retval void
  */
void audio_Stop(void)
{
	I2S_Stop();
	menu_Stop();

	Disable_Tim6();
}

/**
  * @brief  The application .
  * @retval void
  */
void audio_Abort(void)
{
	I2S_Abort();
}

/**
  * @brief  The application .
  * @retval void
  */
void audio_Transfer(uint16_t *pData, uint16_t Size)
{
	I2s_Transfer(pData, Size);
}


/**
  * @brief  The application .
  * @retval void
  */
static void I2s_Start(void)
{
	HAL_StatusTypeDef ret;

	ret = HAL_I2S_DeInit(&hi2s2);
	if(ret == HAL_OK) {
		printf("I2S DeInit OK ...\r\n");
	} else {
		printf("I2S DeInit error -> 0x%x ...\r\n", ret);
	}

	switch(format) {

	case 0x01:
		pAudio->process_buffer = process_wav_buffer;
		break;

	case 0x02:
		pAudio->process_buffer = process_flac_buffer;
		break;

	case 0x03:
		pAudio->process_buffer = process_ape_buffer;
		break;

	default:
		pAudio->process_buffer = error_process_buffer;
		break;
	}

	if(pAudio->bitsPerSamplePerChannel == 16) {

		hi2s2.Init.DataFormat = I2S_DATAFORMAT_16B;
		// fmt:0,LSB(右对齐);1,MSB(左对齐);2,飞利浦标准I2S;3,PCM/DSP;
		// len:0,16位;1,20位;2,24位;3,32位;
		WM8978_I2S_Cfg(2, 0);
		printf("--- DAC Set 16bit ---\r\n");
	} else if(pAudio->bitsPerSamplePerChannel == 24) {

		hi2s2.Init.DataFormat = I2S_DATAFORMAT_32B;

		pAudio->play_size = pAudio->play_size / 2;

		WM8978_I2S_Cfg(2, 2);
		printf("--- DAC Set 24bit ---\r\n");
	} else {
		// printf("\r\nbitsPerSamplePerChannel not supported !\r\n");
		return;
	}

	if(pAudio->sampleRate == 96000) {
		printf("I2S Set 96000 Clock\r\n");
		// I2S_96K_Config();
	} else if(pAudio->sampleRate == 48000) {
		printf("I2S Set 48000 Clock\r\n");
		// I2S_48K_Config();
	} else {
		printf("I2S Set 44100 Clock\r\n");
		// I2S_44K_Config();
	}

	hi2s2.Init.AudioFreq = pAudio->sampleRate;

	ret = HAL_I2S_Init(&hi2s2);
	if(ret == HAL_OK) {

		uint32_t i2sdiv;
		uint32_t i2sodd;
		uint32_t i2sclk;
		uint32_t temp;

		i2sclk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_I2S);
		
		temp = hi2s2.Instance->I2SPR;
		i2sdiv = temp & 0xff;
		i2sodd = (temp & 0x100) >> 8;
		printf("---> i2sclk = %u i2sdiv = %u i2sodd = %u\r\n", i2sclk,  i2sdiv,  i2sodd);

		printf("I2S Init OK ...\r\n");
	} else {
		printf("I2S Init error -> 0x%x ...\r\n", ret);
	}

	ret = HAL_I2S_Transmit_DMA(&hi2s2, pAudio->play_buff, pAudio->play_size);

	if(ret == HAL_OK) {
		printf("I2S DAM started ...\r\n");
	} else {
		printf("I2S DMA error -> 0x%x ...\r\n", ret);
	}
}

/**
  * @brief  The application .
  * @retval void
  */
static void I2s_Transfer(uint16_t *pData, uint16_t Size)
{
	F4_I2S_Transmit_DMA(&hi2s2, pData, Size);
}

/**
  * @brief  The application .
  * @retval void
  */
static void I2S_Pause(void)
{
	HAL_I2S_DMAPause(&hi2s2);
}

/**
  * @brief  The application .
  * @retval void
  */
static void I2S_Resume(void)
{
	HAL_I2S_DMAResume(&hi2s2);
}

/**
  * @brief  The application .
  * @retval void
  */
static void I2S_Stop(void)
{
	HAL_I2S_DMAStop(&hi2s2);
}

/**
  * @brief  The application .
  * @retval void
  */
static void I2S_Abort(void)
{
	HAL_DMA_Abort(hi2s2.hdmatx);
}

/**
  * @brief  The application .
  * @retval void
  */
static void F4_I2S_Transmit_DMA(I2S_HandleTypeDef *hi2s, uint16_t *pData, uint16_t Size)
{
	hi2s->pTxBuffPtr = pData;
	hi2s->TxXferSize = Size;
	hi2s->TxXferCount = Size;

	/*** hdma_spi2_tx.Instance = DMA1_Stream4; ***/

	/* Clear DBM bit */
	DMA1_Stream4->CR &= (uint32_t)(~DMA_SxCR_DBM);

	/* Configure DMA Stream data length */
	DMA1_Stream4->NDTR = Size;

	/* Configure DMA Stream destination address */
	// hdma->Instance->PAR = (uint32_t)&hi2s->Instance->DR;

	/* Configure DMA Stream source address */
	DMA1_Stream4->M0AR = (uint32_t)pData;

	/* Enable Common interrupts*/
	DMA1_Stream4->CR  |= DMA_IT_TC | DMA_IT_TE | DMA_IT_DME;

	/* Enable the Peripheral */
	// __HAL_DMA_ENABLE(hdma);
	DMA1_Stream4->CR |=  DMA_SxCR_EN;

}

/**
  * @brief  The application .
  * @retval void
  */
void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
	Is_Process_buffer = 1;
}

/**
  * @brief  The application .
  * @retval void
  */
void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	Is_Process_buffer = 1;
}


/**
  * @brief  The application .
  * @retval void
  */
static void error_process_buffer(void)
{
	printf("No buffer process defined !!!\r\n");

}

/**
  * @brief  The application .
  * @retval void
  */
void I2S_44K_Config(void)
{
	RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

	__HAL_RCC_SPI2_CLK_DISABLE();

	/** Initializes the peripherals clock
	*/
	PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
	PeriphClkInitStruct.PLLI2S.PLLI2SN = 158;
	PeriphClkInitStruct.PLLI2S.PLLI2SR = 7;
	if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
		Error_Handler();
	}
	HAL_Delay(5);

	__HAL_RCC_SPI2_CLK_ENABLE();
}

/**
  * @brief  The application .
  * @retval void
  */
void I2S_48K_Config(void)
{
	RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

	__HAL_RCC_SPI2_CLK_DISABLE();

	/** Initializes the peripherals clock
	*/
	PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
	PeriphClkInitStruct.PLLI2S.PLLI2SN = 172;
	PeriphClkInitStruct.PLLI2S.PLLI2SR = 7;
	if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
		Error_Handler();
	}
	HAL_Delay(5);

	__HAL_RCC_SPI2_CLK_ENABLE();
}

/**
  * @brief  The application .
  * @retval void
  */
void I2S_96K_Config(void)
{
	RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

	__HAL_RCC_SPI2_CLK_DISABLE();

	/** Initializes the peripherals clock
	*/
	PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S;
	PeriphClkInitStruct.PLLI2S.PLLI2SN = 172;
	PeriphClkInitStruct.PLLI2S.PLLI2SR = 2;
	if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
		Error_Handler();
	}
	HAL_Delay(5);

	__HAL_RCC_SPI2_CLK_ENABLE();
}



